<!DOCTYPE html>
<html>
    <head>
        <title>Pyrefly debugger</title>
<style type="text/css">
body {
    font-family: sans-serif;
    font-size: 10pt;
}

#search {
    float: right;
    width: 400px;
}

.error { color: darkred; }
.result { color: darkblue; }
.key { color: darkgreen; }
.binding { color: dimgray; }
</style>

        <script src="debug.js" type="text/javascript"></script>
        <script type="text/javascript">

const BIG = 100000;

function load() {
    document.getElementById("search").addEventListener('input', search);
    search();
}

function parse(x) {
    const regex = /^((?<lineStart>[0-9]+)(:(?<colStart>[0-9]+))?(-(?<lineEnd>[0-9]+)(:(?<colEnd>[0-9]+))?)?)? *(?<text>.*)$/;
    const re = regex.exec(x).groups;
    const ans = {
        "lineStart": re.lineStart != undefined ? +re.lineStart : 1,
        "colStart": re.colStart != undefined ? +re.colStart : 1,
    };
    if (re.lineEnd == undefined && re.colEnd == undefined) {
        ans.lineEnd = re.lineStart != undefined ? +re.lineStart : BIG;
        ans.colEnd = re.colStart != undefined ? +re.colStart : BIG;
    } else if (re.colEnd == undefined) {
        ans.lineEnd = ans.lineStart;
        ans.colEnd = +re.lineEnd;
    } else {
        ans.lineEnd = +re.lineEnd;
        ans.colEnd = +re.colEnd;
    }
    ans.text = re.text;
    return ans;
}

function rangeOverlap(a, b) {
    // Check if range1 ends before range2 starts
    if (a.lineEnd < b.lineStart || (a.lineEnd === b.lineStart && a.colEnd < b.colStart)) {
        return false;
    }
    // Check if range2 ends before range1 starts
    if (b.lineEnd < a.lineStart || (b.lineEnd === a.lineStart && b.colEnd < a.colStart)) {
        return false;
    }
    // If neither of the above conditions are true, the ranges overlap
    return true;
}

function search() {
    const key = parse(document.getElementById("search").value);
    render(function(range, msg) {
        return rangeOverlap(key, parse(range)) && msg.indexOf(key.text) != -1;
    })
}

function li(texts) {
    const res = document.createElement("li");
    for (var i = 0; i < texts.length; i++) {
        const [style, text, after] = texts[i];
        var x = document.createElement("span");
        x.className = style;
        x.innerText = text;
        res.appendChild(x);
        if (after) {
            res.appendChild(document.createTextNode(after));
        }
    }
    return res;
}

function render(keep) {
    const $errors = document.getElementById("errors");
    const $keys = document.getElementById("keys");
    $errors.innerHTML = "";
    $keys.innerHTML = "";
    for (module_name in data.modules) {
        const module_data = data.modules[module_name];
        for (error in module_data.errors) {
            error = module_data.errors[error];
            if (keep(error.location, error.message)) {
                const item = document.createElement("li");
                $errors.appendChild(li([
                    ["location", error.location, " "],
                    ["error", error.message, ""],
                ]));
            }
        }
        for (binding in module_data.bindings) {
            binding = module_data.bindings[binding];
            if (keep(binding.location, binding.key + " " + binding.binding + " " + binding.result)) {
                const item = document.createElement("li");
                item.textContent = binding.location + " " + binding.key + " = " + binding.binding + " = " + binding.result;
                $keys.appendChild(li([
                    ["location", binding.location, " "],
                    ["key", binding.key, " = "],
                    ["binding", binding.binding, " = "],
                    ["result", binding.result, ""],
                ]));
            }
        }
    }
}

function assertEq(a, b) {
    if (Array.isArray(a) && Array.isArray(b)) {
        assertEq(a.length, b.length);
        for (i = 0; i < a.length; i++) {
            assertEq(a[i], b[i]);
        }
    } else if (typeof a === "object" && typeof b === "object") {
        assertEq(Object.keys(a), Object.keys(b))
        for (k in a) {
            assertEq(a[k], b[k]);
        }
    }
    else if (a !== b) {
        throw "Assertion failure: " + a + " === " + b;
    }
}

function test() {
    assertEq(parse("1:1 test"), {lineStart: 1, colStart: 1, lineEnd: 1, colEnd: 1, text: "test"});
    assertEq(parse("1:2-3:4   more stuff"), {lineStart: 1, colStart: 2, lineEnd: 3, colEnd: 4, text: "more stuff"});
    assertEq(parse("1:2-3"), {lineStart: 1, colStart: 2, lineEnd: 1, colEnd: 3, text: ""});
    assertEq(parse("8 search"), {lineStart: 8, colStart: 1, lineEnd: 8, colEnd: BIG, text: "search"});
    assertEq(parse("just words"), {lineStart: 1, colStart: 1, lineEnd: BIG, colEnd: BIG, text: "just words"});
}

test();

        </script>
    </head>
    <body onload="load()">
        <input id="search" placeholder="Search: e.g. '3-5' or 'foo'" />
        <h1>Pyrefly debugger</h1>
        <h2>Errors</h2>
        <ul id="errors"></ul>
        <h2>Keys</h2>
        <ul id="keys"></ul>
    </body>
</html>
